// * 可迭代对象，这个概念是说任何对象都可以被定制为可在 for...of 循环中使用的对象。
// * Symbol.iterator 的方法（一个专门用于使对象可迭代的内建 symbol），这个方法必须返回一个 迭代器（一个有 next() 方法的对象）。

// 数组、字符串、以及一些内建对象都是可迭代的。
// 普通对象直接迭代（遍历）会报错，需要添加 Symbol.iterator 的方法（一个专门用于使对象可迭代的内建 symbol）。

let obj = {
	from: 1,
	to: 5,
}

// 1. 为对象添加 Symbol.iterator 方法，for..of 调用首先会自动调用这个。
obj[Symbol.iterator] = function () {
	// 它返回【迭代器对象（iterator object）】
	// 2. for...of 仅与下面的迭代器对象一起工作，要求它提供下一个值。
	return {
		current: this.from,
		last: this.to,

		// 3. 【它返回迭代对象】next() 在 for...of 的每一轮循环迭代中被调用。
		next() {
			// 4. 它将返回 {done:.., value:...} 格式的对象
			if (this.current <= this.last) {
				return { done: false, value: this.current++ }
			} else {
				return { done: true }
			}
		},
	}
}

for (const prop of obj) {
	console.log(prop) // 1 2 3 4 5
}

// ***************************************************************************************

// * 使用 对象自身 作为迭代器来简化代码
let range = {
	from: 1,
	to: 5,

	// 添加使对象可迭代的内建 Symbol.iterator 方法（for...of 会首先调用它）
	[Symbol.iterator]() {
		this.current = this.from
		// 返回的是 range 对象自身：它包括了必需的 next() 方法，并通过 this.current 记忆了当前的迭代进程
		return this
	},

	// 添加 next() 方法（迭代对象必须存在 next() 方法）
	next() {
		if (this.current <= this.to) {
			return { done: false, value: this.current++ }
		} else {
			return { done: true }
		}
	},
}

for (const key of range) {
	console.log(key) // 1 2 3 4 5
}

// ***************************************************************************************

// * 字符串可迭代

for (const char of 'test') console.log(char) // t e s t

// ***************************************************************************************

// * 可迭代（遍历） 和 类数组

// * 可迭代：可以应用 for..of 的对象被称为 可迭代对象，技术上说就是实现了 Syumbol.iterator 方法的对象。
// * 类数组：是有 索引 和 length 属性的对象。

// * Array.from(obj[, mapFn, thisArg]) 将 可迭代对象 或 类数组对象 转化为真正的数组。
// 可选参数 mapFn 和 thisArg 允许我们将函数应用到每个元素。

// 1. 可迭代对象 iterator object
let objs = {
	a: 1,
	b: 2,
	[Symbol.iterator]() {
		return this
	},

	next() {
		if (this.a > this.b) {
			return { done: true }
		} else {
			return { done: false, value: this.a++ }
		}
	},
}

console.log(Array.from(objs)) // [ 1, 2 ]

// 2. 类数组对象
let arrayLike = {
	0: 'a',
	1: 'b',
	length: 2,
}

let arr = Array.from(arrayLike)
console.log(arr) // [ 'a', 'b' ]

// 3. 字符串
// 与 str.split 方法不同，它依赖于字符串的可迭代特性。
let str = Array.from('test')
console.log(str) // [ 't', 'e', 's', 't' ]

// 使用 split 方法
console.log('test'.split('')) // [ 't', 'e', 's', 't' ]

// * 字符串截取例子：
let str1 = '𝒳😂𩷶'

// 原生方法不支持识别代理对（译注：UTF-16 扩展字符）
console.log(str1.slice(1, 3)) // ��;

// 这样就对了
function slice(str, start, end) {
	// Array.from 创建代理感知（译注：也就是能够处理 UTF-16 扩展字符的 slice 方法）
	return Array.from(str).slice(start, end).join('')
}

console.log(slice(str1, 1, 3)) // 😂𩷶
